/*
    网页解析模块：
    1、抓取网页
    2、对网页内容解析，提取title、content、url
    3、存储解析结果
*/
#include <iostream>
#include <string>
#include <boost/filesystem.hpp>
#include "log.hpp"
#include "util.hpp"

const std::string src_path = "doc/html"; // 所有抓取到的html网页
const std::string parsed_result_filepath = "doc/parsed_html/parsed_results.txt"; // 保存解析结果的文件

struct DocInfo // 存储解析后结果(一个html对应一个元素)
{
    std::string _title; //标题
    std::string _content; //内容
    std::string _url; //url
};

bool enumFilePath(const std::string& filepath, std::vector<std::string>* files_list);
bool parseHtml(const std::vector<std::string>& files_list, std::vector<DocInfo>* results);
bool saveParsedInfo(const std::string& filename, std::vector<DocInfo>& results);

int main()
{
    // 第一步：枚举src_path路径下所有网页文件（路径+文件名）
    std::vector<std::string> files_list;
    if(!enumFilePath(src_path, &files_list))
    {
        LOG(FATAL, "枚举网页文件失败\n");
        return 1;
    }

    // 第二步：根据files_list中网页文件路径找到每个网页文件，获取网页内容，并解析
    std::vector<DocInfo> results;
    if(!parseHtml(files_list, &results))
    {
        LOG(FATAL, "解析html文件失败\n");
        return 1;
    }

    // 第三步：将解析的结果保存到文件中
    if(!saveParsedInfo(parsed_result_filepath, results))
    {
        LOG(FATAL, "保存解析信息失败\n");
        return 1;
    }
    
    return 0;
}

// 递归枚举所有文件（路径+文件名），保存结果到pfiles_list中
bool enumFilePath(const std::string& filepath, std::vector<std::string>* pfiles_list)
{
    namespace bst_fs = boost::filesystem;

    // 定义一个根目录对象
    bst_fs::path root_path(filepath);
    if(!bst_fs::exists(root_path)) //判断路径是否存在
    {
        LOG(FATAL, "路径不存在\n");
        return false;
    }

    // 定义递归目录迭代器，递归搜索该目录下的所有文件
    bst_fs::recursive_directory_iterator end;
    for(bst_fs::recursive_directory_iterator it(root_path); it != end; it++)
    {
        if(!bst_fs::is_regular_file(*it)) // 判断是否是常规文件
        {
            continue;
        }

        if(it->path().extension() != ".html") // 判断文件路径名的后缀是否是html文件
        {
            continue;
        }

        pfiles_list->push_back(it->path().string());
    }
    return true;
}

// 解析title
bool parseTitle(const std::string& str, std::string* ptitle)
{
    size_t begin = str.find("<title>");
    if(begin == std::string::npos)
        return false;
    
    size_t end = str.find("</title>", begin);
    if(end == std::string::npos)
        return false;
    
    if(begin > end)
        return false;

    begin += strlen("<title>");
    *ptitle = str.substr(begin, end-begin);
    return true;
}

// 解析content
bool parseContent(const std::string& str, std::string* pcontent)
{
    // 基于简易状态机获取内容
    enum
    {
        LABEL,
        CONTENT
    }status;

    status = LABEL;
    for(auto& ch : str)
    {
        switch(status)
        {
            case LABEL:
                if(ch == '>')
                    status = CONTENT;
                break;
            case CONTENT:
                if(ch == '<')
                    status = LABEL;
                else
                    pcontent->push_back(ch);
                break;
            default:
                break;
        }
    }
    return true;
}

// 构建url
bool parseUrl(const std::string& filepath, std::string* purl)
{
    const char* const head_url = "https://www.boost.org/doc/libs/1_78_0/";
    *purl = head_url + filepath;
    return true;
}

// 解析网页（title、内容、url），保存结果到presults
bool parseHtml(const std::vector<std::string>& files_list, std::vector<DocInfo>* presults)
{
    if(files_list.empty())
        return false;

    for(auto& filepath : files_list) //依次解析每一个网页文件
    {
        // 第一步：读取文件
        std::string str;
        if(!ns_util::FileUtil::readFile(filepath, &str))
        {
            LOG(ERROR, "读取文件失败\n");
            continue;
        }
        
        DocInfo result;
        // 第二步：解析title
        if(!parseTitle(str, &result._title))
        {   
            LOG(ERROR, "解析title失败\n");
            continue;
        }
        
        // 第三步：解析content
        if(!parseContent(str, &result._content))
        {
            LOG(ERROR, "解析content失败\n");
            continue;
        }
        
        // 第四步：构建url
        if(!parseUrl(filepath, &result._url))
        {
            LOG(ERROR, "构建url失败\n");
            continue;
        }

        // 第五步：保存解析结果
        presults->push_back(std::move(result));
    }
    return true;
}

// 保存解析结果到文件，results中每一个元素代表一个网页的解析结果
// 每个元素以行为单位保存到文件（建立索引时，方便读取，一行就是一个html文件解析），每个元素内部title、content、url以\3为分隔符分隔
bool saveParsedInfo(const std::string& filename, std::vector<DocInfo>& results)
{
    std::ofstream out(filename, std::ios::out | std::ios::binary);
    if(!out.is_open())
    {
        LOG(FATAL, "打开文件失败\n");
        return false;
    }

    // std::string number_str = std::to_string(results.size()) + "\n";
    // out.write(number_str.c_str(), number_str.size()); // 写入解析了多少个html文件，方便构建索引进度条
    const char* const sep = "\3";
    for(auto& res : results)
    {
        std::string s = res._title + sep + res._content + sep + res._url + "\n";
        out.write(s.c_str(), s.size());
    }

    out.close();
    return true;
}
